#pragma once
#include "Runtime/BattleFirePrefix.h"
namespace Alice
{
    class MathUtils{
    public:
    	static FORCEINLINE bool IsNaN( float A ) { return _isnan(A) != 0; }
    	static FORCEINLINE bool IsNaN(double A) { return _isnan(A) != 0; }
    	static FORCEINLINE bool IsFinite( float A ) { return _finite(A) != 0; }
    	static FORCEINLINE bool IsFinite(double A) { return _finite(A) != 0; }
        /** Computes absolute value in a generic way */
        template< class T > 
        static constexpr FORCEINLINE T Abs( const T A )
        {
            return (A < (T)0) ? -A : A;
        }

        /** Returns 1, 0, or -1 depending on relation of T to 0 */
        template< class T > 
        static constexpr FORCEINLINE T Sign( const T A )
        {
            return (A > (T)0) ? (T)1 : ((A < (T)0) ? (T)-1 : (T)0);
        }

        /** Returns higher value in a generic way */
        template< class T > 
        static constexpr FORCEINLINE T Max( const T A, const T B )
        {
            return (B < A) ? A : B;
        }

        /** Returns lower value in a generic way */
        template< class T > 
        static constexpr FORCEINLINE T Min( const T A, const T B )
        {
            return (A < B) ? A : B;
        }
    	/**
		 * Computes the base 2 logarithm for an integer value.
		 * The result is rounded down to the nearest integer.
		 *
		 * @param Value		The value to compute the log of
		 * @return			Log2 of Value. 0 if Value is 0.
		 */	
    	static constexpr FORCEINLINE uint32 FloorLog2(uint32 Value) 
        {
        	uint32 pos = 0;
        	if (Value >= 1<<16) { Value >>= 16; pos += 16; }
        	if (Value >= 1<< 8) { Value >>=  8; pos +=  8; }
        	if (Value >= 1<< 4) { Value >>=  4; pos +=  4; }
        	if (Value >= 1<< 2) { Value >>=  2; pos +=  2; }
        	if (Value >= 1<< 1) {				pos +=  1; }
        	return pos;
        }

    	/** FloorLog2 but the caller guarantees that Value is not 0. */
    	static constexpr FORCEINLINE uint32 FloorLog2NonZero(uint32 Value)
        {
        	return FloorLog2(Value);
        }

    	/**
		 * Computes the base 2 logarithm for a 64-bit value.
		 * The result is rounded down to the nearest integer.
		 *
		 * @param Value		The value to compute the log of
		 * @return			Log2 of Value. 0 if Value is 0.
		 */	
    	static constexpr FORCEINLINE uint64 FloorLog2_64(uint64 Value) 
        {
        	uint64 pos = 0;
        	if (Value >= 1ull<<32) { Value >>= 32; pos += 32; }
        	if (Value >= 1ull<<16) { Value >>= 16; pos += 16; }
        	if (Value >= 1ull<< 8) { Value >>=  8; pos +=  8; }
        	if (Value >= 1ull<< 4) { Value >>=  4; pos +=  4; }
        	if (Value >= 1ull<< 2) { Value >>=  2; pos +=  2; }
        	if (Value >= 1ull<< 1) {               pos +=  1; }
        	return pos;
        }

    	/** FloorLog2_64 but the caller guarantees that Value is not 0. */
    	static constexpr FORCEINLINE uint64 FloorLog2NonZero_64(uint64 Value)
        {
        	return FloorLog2_64(Value);
        }
		/**
		 * Counts the number of leading zeros in the bit representation of the 8-bit value
		 *
		 * @param Value the value to determine the number of leading zeros for
		 *
		 * @return the number of zeros before the first "on" bit
		 */
		static constexpr FORCEINLINE uint8 CountLeadingZeros8(uint8 Value)
		{
			if (Value == 0) return 8;
			return uint8(7 - FloorLog2(uint32(Value)));
		}

		/**
		 * Counts the number of leading zeros in the bit representation of the 32-bit value
		 *
		 * @param Value the value to determine the number of leading zeros for
		 *
		 * @return the number of zeros before the first "on" bit
		 */
		static constexpr FORCEINLINE uint32 CountLeadingZeros(uint32 Value)
		{
			if (Value == 0) return 32;
			return 31 - FloorLog2(Value);
		}

		/**
		 * Counts the number of leading zeros in the bit representation of the 64-bit value
		 *
		 * @param Value the value to determine the number of leading zeros for
		 *
		 * @return the number of zeros before the first "on" bit
		 */
		static constexpr FORCEINLINE uint64 CountLeadingZeros64(uint64 Value)
		{
			if (Value == 0) return 64;
			return 63 - FloorLog2_64(Value);
		}

		/**
		 * Counts the number of trailing zeros in the bit representation of the value
		 *
		 * @param Value the value to determine the number of trailing zeros for
		 *
		 * @return the number of zeros after the last "on" bit
		 */
		static constexpr FORCEINLINE uint32 CountTrailingZeros(uint32 Value)
		{
			if (Value == 0)
			{
				return 32;
			}
			uint32 Result = 0;
			while ((Value & 1) == 0)
			{
				Value >>= 1;
				++Result;
			}
			return Result;
		}

		/**
		 * Counts the number of trailing zeros in the bit representation of the value
		 *
		 * @param Value the value to determine the number of trailing zeros for
		 *
		 * @return the number of zeros after the last "on" bit
		 */
		static constexpr FORCEINLINE uint64 CountTrailingZeros64(uint64 Value)
		{
			if (Value == 0)
			{
				return 64;
			}
			uint64 Result = 0;
			while ((Value & 1) == 0)
			{
				Value >>= 1;
				++Result;
			}
			return Result;
		}
        /**
         * Returns smallest N such that (1<<N)>=Arg.
         * Note: CeilLogTwo(0)=0 
         */
        static constexpr FORCEINLINE uint32 CeilLogTwo( uint32 Arg )
        {
            // if Arg is 0, change it to 1 so that we return 0
            Arg = Arg ? Arg : 1;
            return 32 - CountLeadingZeros(Arg - 1);
        }

        static constexpr FORCEINLINE uint64 CeilLogTwo64( uint64 Arg )
        {
            // if Arg is 0, change it to 1 so that we return 0
            Arg = Arg ? Arg : 1;
            return 64 - CountLeadingZeros64(Arg - 1);
        }

        /**
         * Returns the smallest N such that (1<<N)>=Arg. This is a less efficient version of CeilLogTwo, but written in a
         * way that can be evaluated at compile-time.
         */
        static constexpr FORCEINLINE uint8 ConstExprCeilLogTwo(SIZE_T Arg)
        {
            if (Arg <= 1)
            {
                return 0;
            }
            // Integer overflow if we tried to add 1 to maximum value, so handle that case separately
            if (Arg + 1 < Arg)
            {
                return sizeof(Arg) * 8;
            }
            return 1 + ConstExprCeilLogTwo((Arg + 1) / 2);
        }

        /** @return Rounds the given number up to the next highest power of two. */
        static constexpr FORCEINLINE uint32 RoundUpToPowerOfTwo(uint32 Arg)
        {
            return 1 << CeilLogTwo(Arg);
        }

        static constexpr FORCEINLINE uint64 RoundUpToPowerOfTwo64(uint64 V)
        {
            return uint64(1) << CeilLogTwo64(V);
        }
    	// Returns e^Value
    	static FORCEINLINE float Exp( float Value ) { return expf(Value); }
    	static FORCEINLINE double Exp(double Value) { return exp(Value); }

    	// Returns 2^Value
    	static FORCEINLINE float Exp2( float Value ) { return powf(2.f, Value); /*exp2f(Value);*/ }
    	static FORCEINLINE double Exp2(double Value) { return pow(2.0, Value); /*exp2(Value);*/ }

    	static FORCEINLINE float Loge( float Value ) {	return logf(Value); }
    	static FORCEINLINE double Loge(double Value) { return log(Value); }

    	static FORCEINLINE float LogX( float Base, float Value ) { return Loge(Value) / Loge(Base); }
    	static FORCEINLINE double LogX(double Base, double Value) { return Loge(Value) / Loge(Base); }
    	static FORCEINLINE float Sqrt( float Value ) { return sqrtf(Value); }
    	static FORCEINLINE double Sqrt( double Value ) { return sqrt(Value); }
    	/** Divides two integers and rounds up */
    	template <class T>
		static constexpr FORCEINLINE T DivideAndRoundUp(T Dividend, T Divisor)
        {
        	return (Dividend + Divisor - 1) / Divisor;
        }

    	/** Divides two integers and rounds down */
    	template <class T>
		static constexpr FORCEINLINE T DivideAndRoundDown(T Dividend, T Divisor)
        {
        	return Dividend / Divisor;
        }

    	/** Divides two integers and rounds to nearest */
    	template <class T>
		static constexpr FORCEINLINE T DivideAndRoundNearest(T Dividend, T Divisor)
        {
        	return (Dividend >= 0)
				? (Dividend + Divisor / 2) / Divisor
				: (Dividend - Divisor / 2 + 1) / Divisor;
        }

    	/**
		 * Computes the base 2 logarithm of the specified value
		 *
		 * @param Value the value to perform the log on
		 *
		 * @return the base 2 log of the value
		 */
    	static FORCEINLINE float Log2(float Value)
        {
        	// Cached value for fast conversions
        	constexpr float LogToLog2 = 1.44269502f; // 1.f / Loge(2.f)
        	// Do the platform specific log and convert using the cached value
        	return Loge(Value) * LogToLog2;
        }

    	/**
		 * Computes the base 2 logarithm of the specified value
		 *
		 * @param Value the value to perform the log on
		 *
		 * @return the base 2 log of the value
		 */
    	static FORCEINLINE double Log2(double Value)
        {
        	// Cached value for fast conversions
        	constexpr double LogToLog2 = 1.4426950408889634; // 1.0 / Loge(2.0);
        	// Do the platform specific log and convert using the cached value
        	return Loge(Value) * LogToLog2;
        }
		/** Clamps X to be between Min and Max, inclusive */
		template< class T >
		static constexpr FORCEINLINE T Clamp(const T X, const T MinValue, const T MaxValue)
		{
			return Max(Min(X, MaxValue), MinValue);
		}
		/** Clamps X to be between Min and Max, inclusive. Explicitly defined here for floats/doubles because static analysis gets confused between template and int versions. */
		static constexpr FORCEINLINE float Clamp(const float X, const float Min, const float Max) { return Clamp<float>(X, Min, Max); }
		static constexpr FORCEINLINE double Clamp(const double X, const double Min, const double Max) { return Clamp<double>(X, Min, Max); }

		/** Clamps X to be between Min and Max, inclusive. Overload to support mixed int64/int32 types. */
		static constexpr FORCEINLINE int64 Clamp(const int64 X, const int32 Min, const int32 Max) { return Clamp<int64>(X, Min, Max); }
		
		/** Returns highest of 3 values */
		template< class T > 
		static constexpr FORCEINLINE T Max3( const T A, const T B, const T C )
		{
			return Max ( Max( A, B ), C );
		}

		/** Returns lowest of 3 values */
		template< class T > 
		static constexpr FORCEINLINE T Min3( const T A, const T B, const T C )
		{
			return Min ( Min( A, B ), C );
		}

		template< class T > 
		static constexpr FORCEINLINE int32 Max3Index( const T A, const T B, const T C )
		{
			return ( A > B ) ? ( ( A > C ) ? 0 : 2 ) : ( ( B > C ) ? 1 : 2 );
		}

		/** Returns index of the lowest value */
		template< class T > 
		static constexpr FORCEINLINE int32 Min3Index( const T A, const T B, const T C )
		{
			return ( A < B ) ? ( ( A < C ) ? 0 : 2 ) : ( ( B < C ) ? 1 : 2 );
		}

		/** Multiples value by itself */
		template< class T > 
		static constexpr FORCEINLINE T Square( const T A )
		{
			return A*A;
		}

		/** Cubes the value */
		template< class T > 
		static constexpr FORCEINLINE T Cube( const T A )
		{
			return A*A*A;
		}
    	static constexpr FORCEINLINE int32 CountBits(uint64 Bits)
    	{
    		// https://en.wikipedia.org/wiki/Hamming_weight
    		Bits -= (Bits >> 1) & 0x5555555555555555ull;
    		Bits = (Bits & 0x3333333333333333ull) + ((Bits >> 2) & 0x3333333333333333ull);
    		Bits = (Bits + (Bits >> 4)) & 0x0f0f0f0f0f0f0f0full;
    		return (Bits * 0x0101010101010101) >> 56;
    	}
    };
} // namespace Alice
